#pragma once
#include "dxapp.h"
#include <xnamath.h>
#include "OBJLoader.h"
#include "Camera.h"
#include <vector>

// globals
const std::string PATH_SHADERS = "..\\ACW_08968\\shaders\\compiled\\";
const std::string PATH_MEDIA   = "..\\ACW_08968\\media\\";

using std::vector;


namespace AlienTypes
{
	enum Value
	{
		VS_ALIEN,
		GS_ALIEN,
		HS_ALIEN,
		EXP_ALIEN,
		BEZ_ALIEN,
		PSP_ALIEN
	};
};

namespace TessTypes
{
	enum Value
	{	
		TESS_SPHERE,
		TESS_WORM,
		TESS_BIRD,
		TESS_MESH
	};
};

namespace TessPartitioning
{
	enum Value
	{
		FRAC_ODD,
		FRAC_EVEN,
		INTEGER,
		POW2
	};
};

typedef AlienTypes::Value		AlienType;
typedef TessTypes::Value		TessType;
typedef TessPartitioning::Value TessPartition;

// Base class for moving objects
struct Animated
{
	Animated() : inMotion(false), targetPos(XMFLOAT3(0,0,0)), xform(XMFLOAT4X4(1.0f, 0.0f, 0.0f, 0.0f,  
																			   0.0f, 1.0f, 0.0f, 0.0f,	
																			   0.0f, 0.0f, 1.0f, 0.0f,  
																			   0.0f, 0.0f, 0.0f, 1.0f))  {}

	void start()
	{
		inMotion = true;
		// update xform
		XMFLOAT3 right, at, pos = getPos();
		XMFLOAT3 up = XMFLOAT3(0.0f, 1.0f, 0.0);

		XMVECTOR vpos = XMLoadFloat3(&pos);
		XMVECTOR vtarget = XMLoadFloat3(&targetPos);

		XMVECTOR vAt = XMVector3Normalize(vtarget - vpos);
		XMVECTOR vRight = XMVector3Normalize(XMVector3Cross(XMLoadFloat3(&up), vAt));

		XMStoreFloat3(&right, vRight);
		XMStoreFloat3(&at, vAt);

		xform._11 = right.x; xform._12 = right.y; xform._13 = right.z;
		xform._21 = up.x;    xform._22 = up.y;    xform._23 = up.z;
		xform._31 = at.x;    xform._32 = at.y;    xform._33 = at.z;

	}

	void updatePos(float dt) 
	{		
		XMFLOAT3 pos = getPos();

		XMVECTOR vpos = XMLoadFloat3(&pos);
		XMVECTOR vtarget = XMLoadFloat3(&targetPos);

		float d = XMVectorGetX(XMVector3Length(vtarget - vpos));
		if (d < 0.01f || !inMotion) {
			inMotion = false;
			return;
		}

		vpos += dt * XMVector3Normalize(vtarget - vpos);

		XMStoreFloat3(&pos, vpos);
		xform._41 = pos.x;
		xform._42 = pos.y;
		xform._43 = pos.z;
	}

	// Getters/Setters
	XMFLOAT3 getTargetPos() 
	{ 
		return targetPos;
	}
	
	void setTargetPos(float x, float y, float z)
	{
		targetPos.x = x; targetPos.y = y; targetPos.z = z;
	}

	void setPos(float x, float y, float z) 
	{
		xform._41 = x; xform._42 = y; xform._43 = z;
	}

	XMFLOAT3 getPos() 
	{ 
		return XMFLOAT3(xform._41, xform._42, xform._43); 
	}

	bool isInMotion() { return inMotion; }

	const XMFLOAT4X4& getMatrix() { return xform; }

	private:																   

		bool		inMotion;																		   	 	
		XMFLOAT3	targetPos;
		XMFLOAT4X4	xform;
};


struct Model
{
	ID3D11Buffer*	vb;
	ID3D11Buffer*	ib;
	unsigned int	numVertices;
	unsigned int	numIndices;
};

struct Mesh 
{
	Material		material;
	Model*			model;
};

struct Alien : public Animated
{
	Mesh		mesh;
	AlienType	type;
	float		expl_time; // for gs aliens
};

struct TessObject
{
	TessPartition partitioning;
	bool	bWireframe;
	UINT	iInsideFactor;
	UINT	iEdgeFactor;
};

struct Sphere : public Animated
{
	float			radius;
	unsigned int	tessFactor;
};


struct Light
{
	XMFLOAT3 dir;
	XMFLOAT3 diffuse;
	XMFLOAT3 specular;
};




class MyApp : public DxApp 
{
	public:
		MyApp();
		~MyApp();

		OBJLoader m_objLoader;

		Alien myAlien;
		bool animationStarted;
		vector<Sphere>	m_spheres;
		vector<Alien>	m_aliens;

		bool initialise(HINSTANCE hInstance, int cmdShow);

		void editTerrain();

		HRESULT buildFX();
		HRESULT buildGeometry();

		void onKey(WPARAM wParam);


		void onLButtonDown();
		void onLButtonUp();
		void onMouseMove(LPARAM lparam);

		void renderScene();
		void updateScene();

	private:
		XMMATRIX _worldMx;
		XMMATRIX _viewMx;
		XMMATRIX _projMx;
		
		Mesh	m_alien;
		Camera  m_camera;

		ID3DX11EffectMatrixVariable *_WorldViewProjMx;

		ID3D11InputLayout *			_inputLayout;
		ID3D11Buffer *				_vertexBuffer;
		ID3D11Buffer *				_indexBuffer;
		ID3D11Buffer*				m_vbSingle;
		ID3D11Buffer*				m_vbControlPoints;
		ID3D11Buffer*				m_ibControlPoints;

		ID3DX11Effect*				m_fxAlienVS;
		ID3DX11Effect*				m_fxAlienGS;
		ID3DX11Effect*				m_fxExplosion;
		ID3DX11Effect*				m_fxImplicit;
		ID3DX11Effect*				m_fxTessSphere;
		ID3DX11Effect*				m_fxTessSingle;
		ID3DX11Effect*				m_fxTessBezierSurface;
		ID3DX11Effect*				m_fxTessPSPSurface;
		ID3DX11Effect*				m_fxTessMesh;
		ID3DX11Effect*				m_fxTessTerrain;
		ID3DX11Effect*				m_fxQuads;
		ID3DX11Effect*				m_fxDynamicTerrain;

		ID3D11Texture2D*			m_texImplicit;
		ID3D11ShaderResourceView*	m_srvImplicit;
		ID3D11RenderTargetView*		m_rtvImplicit;

		ID3D11ShaderResourceView*	m_srvRandom;
		ID3D11ShaderResourceView*	m_srvTerrainHmap;
		ID3D11ShaderResourceView*	m_srvTerrainNmap;

		ID3D11ShaderResourceView*	m_srvSpaceship;

		ID3D11Texture2D*			m_texDynamicHmap;
		ID3D11RenderTargetView*		m_rtvDynamicHmap;
		ID3D11ShaderResourceView*	m_srvDynamicNmap;

		ID3DX11Effect *_fX;
		ID3DX11EffectTechnique *_technique;

		void safeDelete(IUnknown* res);
		bool createImplicitResources();
		bool createEffect(const char* filename, ID3DX11Effect** fx);
		void setMaterial(ID3DX11Effect* fx, const Material& mat);

		void drawMesh(const Mesh& mesh, ID3DX11Effect* fx, const XMFLOAT4X4& mWorld, float time=0.0f);
	
		// tessellation
		void drawTessSphereFromMesh();
		void drawTessSphereFromOct(Sphere* sphere);
		void drawTessBezierSurface(XMFLOAT3 pos, XMFLOAT3 target);
		void drawPSPSurface(XMFLOAT3 pos, XMFLOAT3 target);
		void drawTessMesh(const XMFLOAT4X4& mWorld);
		void drawTessTerrain();

		void drawImplicitObjects();
	
		Sphere mySphere;

		Model	m_alienModel;

		Mesh alienMesh;

		bool	m_LButtonDown;
		bool	m_bPaused;


		void setupScene();

		static const float GRID_WIDTH;
		static const int   N_SPHERES;

		void drawImplicitToTexture();
		const unsigned int m_implicitQuadDimension;

		unsigned int m_mouseX;
		unsigned int m_mouseY;

		Light light;

		void passLightParams(ID3DX11Effect* fx);

		ID3D11RasterizerState*	rs_Wireframe;
};

